﻿#include "Particle.h"

#include <chrono>
#include <cmath>
#include <numbers>
#include <iostream>

#include <vsg/all.h>

#include "DataSingleton.h"
#include "Particle.h"
#include "utilities.h"

vsg::ref_ptr<vsg::Node> Particle::getNode()
{
	using namespace vsg;
	//auto scene = vsg::Group::create();
	m_matrixTransform = vsg::MatrixTransform::create();
	//vsg::GeometryInfo geoInfo;
	//geoInfo.position = { 0.f, 0.f, 0.f };
	//geoInfo.color = m_color;
	//vsg::StateInfo stateInfo;
	//m_matrixTransform->addChild(DataSingleton::instance()->m_builder->createSphere(geoInfo, stateInfo));
	// load shaders
	vsg::Paths searchPaths = vsg::getEnvPaths("VSG_FILE_PATH");
	vsg::ref_ptr<vsg::ShaderStage> vertexShader = vsg::ShaderStage::read(VK_SHADER_STAGE_VERTEX_BIT, "main", vsg::findFile("shaders/simple_vert.spv", searchPaths));
	vsg::ref_ptr<vsg::ShaderStage> fragmentShader = vsg::ShaderStage::read(VK_SHADER_STAGE_FRAGMENT_BIT, "main", vsg::findFile("shaders/simple_frag.spv", searchPaths));
	if (!vertexShader || !fragmentShader)
	{
		std::cout << "Could not create shaders." << std::endl;
	}

	vsg::PushConstantRanges pushConstantRanges{
		{VK_SHADER_STAGE_VERTEX_BIT, 0, 128} // projection view, and model matrices, actual push constant calls automatically provided by the VSG's DispatchTraversal
	};

	vsg::VertexInputState::Bindings vertexBindingsDescriptions{
		VkVertexInputBindingDescription{0, sizeof(vsg::vec3), VK_VERTEX_INPUT_RATE_VERTEX}, // vertex data
		VkVertexInputBindingDescription{1, sizeof(vsg::vec4), VK_VERTEX_INPUT_RATE_VERTEX}, // colour data
	};

	vsg::VertexInputState::Attributes vertexAttributeDescriptions{
		VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0}, // vertex data
		VkVertexInputAttributeDescription{1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0}, // colour data
	};

	vsg::GraphicsPipelineStates pipelineStates{
		vsg::VertexInputState::create(vertexBindingsDescriptions, vertexAttributeDescriptions),
		vsg::InputAssemblyState::create(),
		vsg::RasterizationState::create(),
		vsg::MultisampleState::create(),
		vsg::ColorBlendState::create(),
		vsg::DepthStencilState::create() };

	auto pipelineLayout = vsg::PipelineLayout::create(vsg::DescriptorSetLayouts{}, pushConstantRanges);
	auto graphicsPipeline = vsg::GraphicsPipeline::create(pipelineLayout, vsg::ShaderStages{ vertexShader, fragmentShader }, pipelineStates);
	auto bindGraphicsPipeline = vsg::BindGraphicsPipeline::create(graphicsPipeline);

	// create StateGroup as the root of the scene/command graph to hold the GraphicsProgram, and binding of Descriptors to decorate the whole graph
	auto scenegraph = vsg::StateGroup::create();
	scenegraph->add(bindGraphicsPipeline);


	// add transform to root of the scene graph
	scenegraph->addChild(m_matrixTransform);

	float t_origin = 0.f;
	float t_scale = 1.f;
	float t_top = 1.f;
	vec3 dx = { 0.5f, 0.0f, 0.0f };
	vec3 dy = { 0.0f, 0.5f, 0.0f };
	vec3 dz = { 0.0f, 0.0f, 0.5f };
	vec3 origin = { 0.0f, 0.0f, 0.0f };

	unsigned int num_columns = 20;
	unsigned int num_rows = 10;
	unsigned int num_vertices = num_columns * num_rows;
	unsigned int num_indices = (num_columns - 1) * (num_rows - 1) * 6;

	auto vertices = vec3Array::create(num_vertices);
	auto indices = ushortArray::create(num_indices);;
	m_colors = vsg::vec4Array::create(num_vertices, m_color);
	m_colors->properties.dataVariance = DYNAMIC_DATA_TRANSFER_AFTER_RECORD;

	for (unsigned int r = 0; r < num_rows; ++r)
	{
		float beta = ((float(r) / float(num_rows - 1)) - 0.5f) * PIf;
		float ty = t_origin + t_scale * float(r) / float(num_rows - 1);
		float cos_beta = cosf(beta);
		vec3 dz_sin_beta = dz * sinf(beta);

		vec3 v = dy * cos_beta + dz_sin_beta;
		vec3 n = normalize(v);

		unsigned int left_i = r * num_columns;
		vertices->set(left_i, v + origin);

		unsigned int right_i = left_i + num_columns - 1;
		vertices->set(right_i, v + origin);

		for (unsigned int c = 1; c < num_columns - 1; ++c)
		{
			unsigned int vi = left_i + c;
			float alpha = (float(c) / float(num_columns - 1)) * 2.0f * PIf;
			v = dx * (-sinf(alpha) * cos_beta) + dy * (cosf(alpha) * cos_beta) + dz_sin_beta;
			n = normalize(v);
			vertices->set(vi, origin + v);
		}
	}

	unsigned int i = 0;
	for (unsigned int r = 0; r < num_rows - 1; ++r)
	{
		for (unsigned int c = 0; c < num_columns - 1; ++c)
		{
			unsigned lower = num_columns * r + c;
			unsigned upper = lower + num_columns;

			indices->set(i++, lower);
			indices->set(i++, lower + 1);
			indices->set(i++, upper);

			indices->set(i++, upper);
			indices->set(i++, lower + 1);
			indices->set(i++, upper + 1);
		}
	}

	// setup geometry
	auto drawCommands = vsg::Commands::create();
	drawCommands->addChild(vsg::BindVertexBuffers::create(0, vsg::DataList{ vertices, m_colors }));
	drawCommands->addChild(vsg::BindIndexBuffer::create(indices));
	drawCommands->addChild(vsg::DrawIndexed::create(num_indices, 1, 0, 0, 0));

	m_matrixTransform->addChild(drawCommands);

	return scenegraph;
}

void Particle::update(const std::chrono::nanoseconds& delt) {
	if (isDone()) {
		return;
	}

	m_duration += delt;

	if (m_radius < 0.003 * m_A && m_duration.count() * 1e-9 > 1.f) {
		invalid();
		return;
	}

	double delt_seconds = delt.count() * 1e-9;

	double acceleration_x = -(m_dragCoefficient)*m_speed.x;
	double acceleration_y = -(m_dragCoefficient)*m_speed.y;
	double acceleration_z = -(m_dragCoefficient)*m_speed.z;

	// x = v0*t + 1/2*a*t^2
	double dx = m_speed.x * delt_seconds + 0.5 * acceleration_x * pow(delt_seconds, 2.0);
	double dy = m_speed.y * delt_seconds + 0.5 * acceleration_y * pow(delt_seconds, 2.0);
	double dz = m_speed.z * delt_seconds + 0.5 * acceleration_z * pow(delt_seconds, 2.0);

	m_pos.x += dx;
	m_pos.y += dy;
	m_pos.z += dz;

	// v = v0 + a*t
	m_speed.x += acceleration_x * delt_seconds;
	m_speed.y += acceleration_y * delt_seconds;
	m_speed.z += acceleration_z * delt_seconds;

	double t = m_duration.count() * 1e-9;

	// 以半径的变化设定颜色饱和度和亮度
	double A = m_A * 1.0f / (sqrt(2. * std::numbers::pi) * m_theta); // 正态分布最大值
	double left_value = 0.5;
	double right_value = 1;
	double span = right_value - left_value;
	double hsvRatio = m_radius / A;
	double deltH = DataSingleton::instance()->h_k * t;
	vsg::dvec3 hsv = ColorToHSV(m_color);
	hsv.x += deltH;
	hsv.y = left_value + span * hsvRatio;
	hsv.z = left_value + span * hsvRatio;
	m_color = ColorFromHSV(hsv.x, hsv.y, hsv.z);
	for (auto& color : *m_colors) {
		color.set(m_color.r, m_color.g, m_color.b, m_color.a);
	}
	m_colors->dirty();

	// 半径以正态方程变化
	m_radius = A * exp(-pow(t - m_mu, 2.0) / (2.0 * pow(m_theta, 2.0)));

	m_matrixTransform->matrix = vsg::translate(m_pos) * vsg::scale(m_radius, m_radius, m_radius);
}

void Particle::activate()
{
	m_state = ParticleState::Flying;
	m_duration -= m_duration;

	// y = A*a**2
	// y = A*(1+D*deltT)**x
	// speed = initSpeed * (1+dragCoefficeint*deltT)**n
	// n = log_(1+deltT)(y/A)
	// 根据初始速度计算出速度接近于0所需的时间，设定为正态方程的均值，以实现在烟花粒子近乎静止时，烟花粒子的半径达到最大，亮度最高
	double deltT = 0.001;
	double speedEnd = 7;
	double D = -m_dragCoefficient;
	double initSpeed = length(m_speed);
	double a = 1 + D * deltT;
	double n = log(speedEnd / initSpeed) / log(a);
	double t = n * deltT * 2;
	m_mu = t;
}

void Particle::invalid()
{
	m_state = ParticleState::Done;
}


